package com.fm.webrtc.connection;

import static org.webrtc.PeerConnection.SignalingState.HAVE_REMOTE_OFFER;

import android.content.Context;
import android.media.MediaCodecInfo;
import android.util.Log;

import com.fm.mediasoup.callback.CallbackMediaStream;
import com.fm.mediasoup.callback.CallbackPeerMessage;
import com.fm.mediasoup.callback.CallbackSdp;
import com.fm.mediasoup.message.SdpMessage;
import com.fm.webrtc.message.CandidateMessage;
import com.fm.mediasoup.util.Camera;

import org.json.JSONObject;
import org.webrtc.AudioTrack;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CandidatePairChangeEvent;
import org.webrtc.DataChannel;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionDependencies;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.Predicate;
import org.webrtc.RTCStats;
import org.webrtc.RTCStatsCollectorCallback;
import org.webrtc.RTCStatsReport;
import org.webrtc.RtpParameters;
import org.webrtc.RtpTransceiver;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
import org.webrtc.audio.JavaAudioDeviceModule;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

public class MediasoupPeerConnection implements org.webrtc.PeerConnection.Observer,SdpObserver, CallbackPeerMessage, RTCStatsCollectorCallback {

    private PeerConnectionFactory.InitializationOptions initializationOptions;
    private Context context;
    private int width = 480;
    private int hegiht = 320;
    private int fps = 15;
    private int peerId = 0;
    private Timer timer;
    private CallbackSdp callbackSdp;
    private static MediasoupPeerConnection peerConnectionObject = null;
    private ArrayList<org.webrtc.PeerConnection.IceServer> iceServerList;
    private org.webrtc.PeerConnection.IceServer iceServer;
    private PeerConnectionFactory peerConnectionFactory;
    private org.webrtc.PeerConnection peerConnection = null;
    private static final String stun = "stun:stun.l.google.com:19302";
    private org.webrtc.PeerConnection.RTCConfiguration rtcConfig;
    private MediaStream mediaStream;
    private SurfaceTextureHelper surfaceTextureHelper;
    private EglBase.Context eglBaseContext;
    private  SurfaceViewRenderer svrLocal;
    private SurfaceViewRenderer remoteVideo;
    private VideoSource videoSource;

    public PeerConnection.IceGatheringState getIceGatheringState() {
        return iceGatheringState;
    }

    private org.webrtc.PeerConnection.IceGatheringState iceGatheringState = PeerConnection.IceGatheringState.NEW;
    private  List<String> streamList = new ArrayList<>();

    private  VideoCapturer videoCapturer = null;

    public CallbackMediaStream getCallbackMediaStream() {
        return callbackMediaStream;
    }

    private CallbackMediaStream callbackMediaStream;

    public MediasoupPeerConnection(Context context, SurfaceTextureHelper surfaceTextureHelper, EglBase.Context eglBaseContext, SurfaceViewRenderer svrLocal, CallbackMediaStream callbackMediaStream){
        this.context = context;
        this.surfaceTextureHelper = surfaceTextureHelper;
        this.eglBaseContext = eglBaseContext;
        this.svrLocal = svrLocal;
        this.callbackMediaStream = callbackMediaStream;
    }

    public void initPeerConnection(){
        initializationOptions = PeerConnectionFactory.InitializationOptions.builder(this.context)
               // .setEnableInternalTracer(true)
                .createInitializationOptions();

        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        options.networkIgnoreMask = 0;
        options.disableNetworkMonitor = true;

        PeerConnectionFactory.initialize(initializationOptions);

        JavaAudioDeviceModule audioDeviceModule = JavaAudioDeviceModule.builder(this.context).createAudioDeviceModule();

        // 创建一个 Predicate 对象，用于过滤编解码器
        Predicate<MediaCodecInfo> codecFilter = new Predicate<MediaCodecInfo>() {
            @Override
            public boolean test(MediaCodecInfo codecInfo) {
                // 进行编解码器过滤的条件判断
                Log.e("MediaCodecInfo", codecInfo.getName());
                // 返回 true 表示编解码器符合条件，返回 false 表示编解码器不符合条件
                // 可根据需要进行自定义判断，例如根据编解码器的名称、支持的媒体类型等进行过滤
                //return codecInfo.getName().startsWith("video/") && codecInfo.getCapabilitiesForType("video/vp8") != null;
                return false;
            }
        };

        peerConnectionFactory = PeerConnectionFactory.builder()
                .setAudioDeviceModule(audioDeviceModule)
                .setVideoDecoderFactory(new DefaultVideoDecoderFactory(this.eglBaseContext))
                .setVideoEncoderFactory(new VideoEncoderFactory(this.eglBaseContext, false, false,codecFilter))
                .createPeerConnectionFactory();





        iceServerList = new ArrayList<>();

        iceServer = org.webrtc.PeerConnection.IceServer.builder(stun).createIceServer();
       // iceServerList.add(iceServer);

        rtcConfig = new org.webrtc.PeerConnection.RTCConfiguration(iceServerList);
        rtcConfig.rtcpMuxPolicy =  PeerConnection.RtcpMuxPolicy.REQUIRE;
        rtcConfig.iceTransportsType = PeerConnection.IceTransportsType.ALL;
        rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
        rtcConfig.sdpSemantics = org.webrtc.PeerConnection.SdpSemantics.UNIFIED_PLAN;
        streamList = new ArrayList<>();
        streamList.add("stream_id");
        PeerConnectionDependencies peerConnectionDependencies = PeerConnectionDependencies.builder(this).createPeerConnectionDependencies();
        peerConnection = peerConnectionFactory.createPeerConnection(rtcConfig, peerConnectionDependencies);
    }

    public void addAudioTrack(){
        AudioTrack audioTrack = peerConnectionFactory.createAudioTrack("audio_label", peerConnectionFactory.createAudioSource(new MediaConstraints()));
        audioTrack.setEnabled(true);
        audioTrack.setVolume(10);
        peerConnection.addTrack(audioTrack, streamList);
    }

    public void addVideoTrack(){
        videoCapturer = Camera.createCameraCapturer(new Camera2Enumerator(context));
        if (videoCapturer != null) {
            videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
            VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("video_label", videoSource);
            videoTrack.setEnabled(true);
            videoTrack.addSink(svrLocal);
            peerConnection.addTrack(videoTrack, streamList);
            RtpTransceiver.RtpTransceiverInit rtpTransceiverInit = new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY);

//            peerConnection.addTransceiver(videoTrack, rtpTransceiverInit);

        }
    }

    public void startVideoCapturer(){
        RTCStatsCollectorCallback rtcStatsCollectorCallback = this;

        timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                peerConnection.getStats(rtcStatsCollectorCallback);
            }
        },0, 1000);
        videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
        videoCapturer.startCapture(width, hegiht, fps);
    }

    @Override
    public void onSignalingChange(org.webrtc.PeerConnection.SignalingState signalingState) {
        Log.e("onSignalingChange", signalingState.name());
    }

    @Override
    public void onIceConnectionChange(org.webrtc.PeerConnection.IceConnectionState iceConnectionState) {
        Log.e("onIceConnectionChange", iceConnectionState.name());
        iceGatheringState = iceGatheringState;
    }

    @Override
    public void onIceConnectionReceivingChange(boolean b) {
        Log.e("fuweida", "onIceConnectionReceivingChange");
    }

    @Override
    public void onTrack(RtpTransceiver transceiver) {
        List<RtpParameters.Encoding> encodingList = transceiver.getSender().getParameters().encodings;
        for (RtpParameters.Encoding encoding : encodingList) {
            Log.e("encoding", encoding.toString());
        }
    }

    @Override
    public void onIceGatheringChange(org.webrtc.PeerConnection.IceGatheringState iceGatheringState) {
        Log.e("onIceGatheringChange", iceGatheringState.name());
        this.iceGatheringState = iceGatheringState;

    }

    @Override
    public void onIceCandidate(IceCandidate iceCandidate) {
        Log.e("fuweida", "onIceCandidate");
        Log.e("ice", peerConnection.iceConnectionState().name());
        String sdp = iceCandidate.sdp;
        String sdpMid = iceCandidate.sdpMid;
        int sdpMLineIndex = iceCandidate.sdpMLineIndex;
        CandidateMessage candidateMessage = new CandidateMessage(sdp, sdpMLineIndex, sdpMid);

//        try {
//            MainActivity.sendMessage(this.peerId, candidateMessage.toJson().toString());
//        } catch (JSONException e) {
//            e.printStackTrace();
//        }
        Log.e("fuweida", candidateMessage.toString());

    }

    @Override
    public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
        Log.e("fuweida", "onIceCandidatesRemoved");

    }

    @Override
    public void onAddStream(MediaStream mediaStream) {
        Log.e("mediaStream", mediaStream.getId());
        callbackMediaStream.addStream(mediaStream);
    }

    @Override
    public void onRemoveStream(MediaStream mediaStream) {

    }

    @Override
    public void onDataChannel(DataChannel dataChannel) {

    }

    @Override
    public void onRenegotiationNeeded() {

    }

    @Override
    public void onCreateSuccess(SessionDescription sessionDescription) {
        Log.e("fuweida", "onCreateSuccess");
        String type = "";
        if (sessionDescription.type == SessionDescription.Type.OFFER) {
            type = "offer";
        } else if (sessionDescription.type == SessionDescription.Type.ANSWER) {
            type = "answer";
        }
        SdpMessage sdpMessage = new SdpMessage(type, sessionDescription.description);
        if (sdpMessage.getType().equals("offer")) {
            peerConnection.setLocalDescription(this, sessionDescription);
            this.callbackSdp.offer(sdpMessage);
        } else {
            this.callbackSdp.answer(sdpMessage);
            peerConnection.setLocalDescription(this, sessionDescription);
        }
    }

    @Override
    public void onSetSuccess() {

    }

    @Override
    public void onSelectedCandidatePairChanged(CandidatePairChangeEvent event) {


    }

    @Override
    public void onCreateFailure(String s) {
        Log.e("fuweida", s);
    }

    @Override
    public void onSetFailure(String s) {
        Log.e("fuweda onSetFailure", s);
    }

    private String getStringKey(JSONObject jsonObject, String name){
        try {
            return jsonObject.getString(name);
        }catch (Exception e) {
            return null;
        }
    }

    private Integer getIntegerKey(JSONObject jsonObject, String name) {
        try {
            return jsonObject.getInt(name);
        }catch (Exception e) {
            return null;
        }
    }

    public void createAnswer(CallbackSdp callbackSdp){
        //initPeerConnection();
        this.callbackSdp = callbackSdp;
        peerConnection.createAnswer(this, new MediaConstraints());
    }

    public void createOffer(CallbackSdp callbackSdp){
        this.callbackSdp = callbackSdp;
        peerConnection.createOffer(this, new MediaConstraints());
    }

    public void setLocalDescription(String sdp){
        SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.OFFER, sdp);
        Log.e("setRemoteDescription", sessionDescription.description.toString());
        peerConnection.setRemoteDescription(this, sessionDescription);
    }

    public void setRemoteDescription(String sdp){
        SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.ANSWER, sdp);
        Log.e("setRemoteDescription", sessionDescription.description.toString());
        peerConnection.setRemoteDescription(this, sessionDescription);
    }

    public void release(){
        try {
            if (timer != null) timer.cancel();
            if (peerConnection != null) {
                peerConnection.dispose();
            }
            if (videoCapturer != null)
                videoCapturer.stopCapture();

            peerConnectionObject = null;
        }catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public void message(int peer_id, String message) {
        this.peerId = peer_id;
        //Log.e("fuweicong", String.valueOf(peer_id));
        Log.d("fuweicong", message);
        try{
            JSONObject jsonObject  = new JSONObject(message);

            String sdp = getStringKey(jsonObject, "sdp");
            String type = getStringKey(jsonObject, "type");

            String candidate = getStringKey(jsonObject, "candidate");
            String sdpMin = getStringKey(jsonObject, "sdpMid");
            Integer sdpMLineIndex = getIntegerKey(jsonObject, "sdpMLineIndex");

            if (type != null) {
                initPeerConnection();
                SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.OFFER, sdp);
                peerConnection.setRemoteDescription(this, sessionDescription);
                if(peerConnection.signalingState() == HAVE_REMOTE_OFFER) {
                    peerConnection.createAnswer(this, new MediaConstraints());
                } else {
                    Log.e("fuweicong", "HAVE_REMOTE_OFFER");
                }

                Log.e("fuweida", sdp);
            } else if (candidate != null && sdpMin != null && sdpMLineIndex != null){
                Log.e("fuweida", candidate);
                IceCandidate iceCandidate = new IceCandidate(sdpMin, sdpMLineIndex, candidate);
                peerConnection.addIceCandidate(iceCandidate);
            }
        }catch (Exception e) {
            Log.e("fuweicong", e.getMessage());
        }

    }

    @Override
    public void onStatsDelivered(RTCStatsReport rtcStatsReport) {
        Map<String, RTCStats> stringRTCStatsMap = rtcStatsReport.getStatsMap();

        for (Map.Entry<String, RTCStats> rtcStatsEntry : stringRTCStatsMap.entrySet()) {
//            Log.e("RTCStatsReport", rtcStatsEntry.getKey());
           if (rtcStatsEntry.getValue().getType().equals("outbound-rtp")) {
//               Log.e("qualityLimitationReason", rtcStatsEntry.getValue().getMembers().get("packetsSent").toString());
//
//               Log.e("qualityLimitationReason", rtcStatsEntry.getValue().getMembers().get("qualityLimitationReason").toString());
//               Log.e("totalPacketSendDelay", rtcStatsEntry.getValue().getMembers().get("totalPacketSendDelay").toString());
//               long milliseconds = (long) (rtcStatsEntry.getValue().getTimestampUs() / 1000); // 将微秒数转换为毫秒数
//                Date date = new Date(milliseconds); // 使用毫秒数创建Date对象
//
//                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); // 指定时间格式
//                String timeString = sdf.format(date); // 将Date对象格式化为时间字符串

            }

        }
    }
}
